package factory;

import Annotation.Autowired;
import Annotation.Scope;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 添加依赖
 */
public class Assignment {
    //对外访问的方法
    private static Map<String , Object> map=new HashMap<String, Object>();
    public static <T> T fun(String jianzhi) throws Exception{
        Object init = init(jianzhi);
        return (T)init;
    }

    /**
     *拿到被注入依赖后的对象
     */
    private static <T> T init(String jianzhi) throws Exception {
        List<String> init = BeanName.init();
        Class<?> clazz=null;
        //初始化单例map集合
        for(String str:init){
            clazz=Class.forName(str);
            //执行初始化方法
            NewBean.initialization(clazz);
            if(clazz.isAnnotationPresent(Scope.class)){
                //实例化所有带Scope的类
                NewBean.getBean(clazz);
            }
        }
        //哪的测试类想取出类的元对象
        Class<?> beanName = getBeanName(init, jianzhi);
        //通过工场拿到它的实例
        Object bean1 = NewBean.getBean(beanName);
        //给它的字段注入依赖
        fieldInjection(init, bean1);
        //给它的方法注入依赖
        methodInjection(init,bean1);
        return (T)bean1;
    }

    /**
     * 注入依赖
     * 参数1：所有的包名
     * 参数2：测试类想要得到类的实例(注入递归的时候不能实例化，所以必须传对象)
     */
    private static void fieldInjection(List<String> init , Object bean1) throws Exception{
        //获取他的元对象
        Class<?> beanName = bean1.getClass();
        //获取要注入类的所有字段
        Field[] declaredField = beanName.getDeclaredFields();
        for(int i=0;i<declaredField.length;i++){
            if(declaredField[i].isAnnotationPresent(Autowired.class)){
                //注入对象的键值
                String value = declaredField[i].getAnnotation(Autowired.class).value();
                //通过注释取对应的Class<?>值
                Class<?> injection = getBeanName(init, value);
                //要注入的实例化对象
                Object bean = NewBean.getBean(injection);
                declaredField[i].setAccessible(true);
                //赋值
                declaredField[i].set(bean1,bean);

                /********判断注入对象是否需要添加依赖*********/
                Field[] declaredField1 = injection.getDeclaredFields();
                for(int it=0;it<declaredField1.length;it++){
                    if(declaredField1[it].isAnnotationPresent(Autowired.class)){
                        //递归调用
                        fieldInjection(init,bean);
                    }
                }
            }
        }
    }

    /**
     * 通过方法注入依赖
     * 参数1：所有的包名
     * 参数2：测试类想要得到类的实例
     */
    private static void methodInjection(List<String> init,Object bean1) throws Exception {
        //获取他的元对象
        Class<?> beanName = bean1.getClass();
        //获取要注入类的所有方法
        Method[] declaredMethods = beanName.getDeclaredMethods();
        for(int i=0;i<declaredMethods.length;i++){
            if(declaredMethods[i].isAnnotationPresent(Autowired.class)){
                String value = declaredMethods[i].getAnnotation(Autowired.class).value();
                //要注入的元对象
                Class<?> beanName1 = getBeanName(init,value);
                //通过工场实例化要注入的元对象
                Object bean = NewBean.getBean(beanName1);
                declaredMethods[i].setAccessible(true);
                declaredMethods[i].invoke(bean1,bean);

                /********判断注入对象是否需要添加依赖*********/
                Method[] declaredMethods1 = beanName1.getDeclaredMethods();
                for(int it=0;it<declaredMethods1.length;it++){
                    if(declaredMethods1[it].isAnnotationPresent(Autowired.class)){
                        methodInjection(init,bean);
                    }
                }
            }
        }

    }

    /**
     *通过注释取Class<?>值
     */
    private static Class<?> getBeanName(List<String> init,String strs) throws Exception{
        Class<?> clazz = null;
        //把注释值的首字母大写，变为一个简单类名
        String st=strs.substring(0, 1).toUpperCase() + strs.substring(1);
        for(String str:init){
            //看所有的全限定类名中是否包简单类名
            if(str.contains(st)){
                clazz = Class.forName(str);
            }
        }
        return clazz;
    }
}
